Skip to content

Publish versioning, roadmap, and dependency policies for v2 - #3215

Open
maxisbey wants to merge 2 commits into
mainfrom
docs/tier1-governance-files
Open

Publish versioning, roadmap, and dependency policies for v2#3215
maxisbey wants to merge 2 commits into
mainfrom
docs/tier1-governance-files

Conversation

@maxisbey

Copy link
Copy Markdown
Contributor

The versioning policy, roadmap, and dependency policy that the SEP-1730 tier assessment relies on were written to v1.x (#2084) after the branches diverged and never came forward, so the default branch has been without them since v2 became the default. Rather than port them — v1.x's roadmap links the TypeScript project board and dates v2 as "alpha ~mid-March", and its versioning doc describes a release model that no longer exists — this writes them for v2 as it actually stands.

Motivation and Context

  • docs/versioning.md ("Versioning and support policy") — SemVer semantics in PEP 440 syntax, mcp / mcp-types lockstep, the public-API boundary (__all__ plus documented paths; message text is not API), what is and is not a breaking change, provisional/experimental as the two carve-outs, and the two deprecation channels (@deprecated for SDK APIs, MCPDeprecationWarning for spec-retired features). Links SECURITY.md for the supported-lines table.
  • docs/roadmap.md — organized by spec revision: the 2026-07-28 project board (Types Rework #42) and its one open item (Capabilities API + server/discover handler #2896), then the not-yet-implemented set, which is exactly the conformance expected-failures baseline — the tasks extension (SEP-2663, Implement SEP-2663: Tasks Extension #2806), DPoP (SEP-1932), and the jwt-bearer grant — plus the v1.x maintenance stance.
  • DEPENDENCY_POLICY.md (root, since docs/dependency-policy.md is the only docs path the tier check reads and it isn't a natural nav page) — floors-only requirements, the mcp-types exact pin explained, why floors don't move for CVEs (the AGENTS.md stance, with the uvicorn and mitigate CVE-2025-62727 for starlette #1552 reasoning linked), the lowest-direct CI leg that keeps floors honest, and the Dependabot uv/actions setup.
  • CONTRIBUTING.md — an "Issue Triage" section (two-business-day first label, the shared taxonomy, P0 = resolved within 7 days) — the SLA text SEP-1730 measures, which the rewrite had dropped — and a pointer to the dependency policy. RELEASE.md's bump recipe links it too.

Two policy statements in the versioning page are worth an explicit look, since they are commitments rather than descriptions:

  1. Dropping a Python version after its upstream EOL ships in a minor release with a release-note callout, not a major. (v1.x's VERSIONING.md said the opposite; the ecosystem norm is this way, and tying 2.x to Python 3.10 until 3.0 seemed the wrong trade.)
  2. The exact text of log lines, warnings, and exception messages is declared not part of the API — types and documented raise conditions are.

How Has This Been Tested?

scripts/docs/build.sh (strict Zensical build + cross-reference check) green with the two new pages in the nav; markdownlint clean.

Breaking Changes

None.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Deliberately not in this PR: the feature-documentation gaps against the tier audit's 48-feature list (a docs PR of their own), the v1.x roadmap's stale content, and a CHANGELOG.md (GitHub Releases remain the changelog, as on every other SDK).

AI Disclaimer

The versioning policy, roadmap, and dependency policy that the SEP-1730
tier assessment relies on were written to the v1.x branch and never
came forward to main. Add them for v2 as they actually stand: a
docs page stating the version scheme, the public-API boundary, what
counts as breaking, and the two deprecation channels; a roadmap page
pointing at the 2026-07-28 project board and the extension work
still open; and a root DEPENDENCY_POLICY.md describing the floors-only
requirement style and the automated-update setup. CONTRIBUTING.md gets
the issue-triage section (label taxonomy, priority commitments) that
its rewrite dropped, and RELEASE.md links the new dependency policy
from the bump recipe.

No-Verification-Needed: doc-only change
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

📚 Documentation preview

Preview https://pr-3215.mcp-python-docs.pages.dev
Deployment https://45ffe26a.mcp-python-docs.pages.dev
Commit e8bee4d
Triggered by @maxisbey
Updated 2026-08-10 17:27:22 UTC

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/versioning.md">

<violation number="1" location="docs/versioning.md:13">
P3: Requirements that explicitly name a prerelease (for example `mcp>=2.1.0b1`) do not need to be exact pins, and resolvers may also use a prerelease when no final release satisfies the requirement. This “never” statement can cause users to misdiagnose why a prerelease was installed; describe the normal exclusion rule and its full exceptions instead.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread docs/versioning.md Outdated
Comment thread docs/roadmap.md Outdated
Comment thread docs/versioning.md Outdated
* **`X` (minor)** — new functionality and every non-breaking change.
* **`Y` (patch)** — bug fixes only.
* **The leading `2` (major)** — the only place a breaking change to the public API can land.
* **Pre-releases** are cut from `main` as `2.X.YaN` (alpha), `2.X.YbN` (beta), and `2.X.YrcN` (release candidate). Installers never select a pre-release unless you ask for one, by exact pin or `--pre`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Requirements that explicitly name a prerelease (for example mcp>=2.1.0b1) do not need to be exact pins, and resolvers may also use a prerelease when no final release satisfies the requirement. This “never” statement can cause users to misdiagnose why a prerelease was installed; describe the normal exclusion rule and its full exceptions instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/versioning.md, line 13:

<comment>Requirements that explicitly name a prerelease (for example `mcp>=2.1.0b1`) do not need to be exact pins, and resolvers may also use a prerelease when no final release satisfies the requirement. This “never” statement can cause users to misdiagnose why a prerelease was installed; describe the normal exclusion rule and its full exceptions instead.</comment>

<file context>
@@ -0,0 +1,77 @@
+* **`X` (minor)** — new functionality and every non-breaking change.
+* **`Y` (patch)** — bug fixes only.
+* **The leading `2` (major)** — the only place a breaking change to the public API can land.
+* **Pre-releases** are cut from `main` as `2.X.YaN` (alpha), `2.X.YbN` (beta), and `2.X.YrcN` (release candidate). Installers never select a pre-release unless you ask for one, by exact pin or `--pre`.
+
+`mcp` and its wire-types package [`mcp-types`](https://pypi.org/project/mcp-types/) release in lockstep at the same version: each `mcp` release requires exactly the matching `mcp-types` (`mcp-types==2.X.Y`).
</file context>

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beyond the three inline findings, two candidate inconsistencies in the new CONTRIBUTING.md triage section were examined and ruled out: the status-label list omitting needs maintainer action (that label is discussed earlier in the file as a not-ready marker, not part of the shared status taxonomy the section documents), and the taxonomy differing from the labels the issue templates auto-apply (templates apply intake labels; the section describes the maintainer-applied taxonomy at triage time).

Extended reasoning...

This run found three nit-level wording/precision issues in the new policy docs, all posted as inline comments. Beyond those, finder agents raised two candidate contradictions in the CONTRIBUTING.md Issue Triage section, and I cross-checked them against the file and .github/ISSUE_TEMPLATE/: the templates auto-apply intake labels (question, v2, feature request, need confirmation) while the new section documents the shared maintainer-applied triage taxonomy, and the needs-maintainer-action mention is pre-existing text about work-readiness rather than the triage status set — so neither is a real inconsistency introduced by this PR. Recording this so a later pass does not re-explore the same candidates; it is informational only, not a correctness guarantee.

Comment thread DEPENDENCY_POLICY.md

Every declared floor is exercised: CI runs the full test suite both against the locked dependency set and against a `lowest-direct` resolution, on every supported Python version, so a floor that has quietly become false fails the build rather than a user's install.

Raising a floor within the same major version of a dependency is a minor-release change and is called out in the release notes; see the [versioning policy](https://py.sdk.modelcontextprotocol.io/versioning/). Adding a new required runtime dependency is a maintainer decision made in an issue before the pull request, not a side effect of a feature.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The two new policy docs answer the same question differently: DEPENDENCY_POLICY.md scopes the minor-release permission to floor raises "within the same major version of a dependency", while docs/versioning.md's non-breaking list allows any minimum-version raise (no cross-major carve-out) — and each page defers to the other as the authority. Either drop the qualifier here or add the cross-major case explicitly to versioning.md's breaking-change list, so a reader can tell whether e.g. moving to pydantic>=3 may land in a 2.X.0 release.

Extended reasoning...

The inconsistency. This PR adds two policy documents that both make a normative statement about when a dependency floor raise may ship in a minor release, and they don't agree on scope. docs/versioning.md (line 46, under "These do not [count as breaking], and can ship in a minor release") says unconditionally: "raising a dependency's minimum version when the SDK needs newer functionality … called out in the release notes (the dependency policy covers the first)". DEPENDENCY_POLICY.md line 18 instead says: "Raising a floor within the same major version of a dependency is a minor-release change and is called out in the release notes; see the versioning policy."

Why the qualifier matters. In a document whose stated purpose (per the PR description) is to publish commitments rather than descriptions — read by the SEP-1730 tier assessment — a scoped qualifier reads as deliberate. "Within the same major version" plainly implies that raising a floor across a dependency's major version is a different, larger kind of change. That reading is reinforced by the same file already treating dependency majors specially ("no upper bounds unless a dependency's next major version is known to break the SDK"). versioning.md, by contrast, admits any needed-functionality floor raise into a minor with no such carve-out.

Why a reader can't resolve it. The deferral is circular: versioning.md says "the dependency policy covers the first", and DEPENDENCY_POLICY.md says "see the versioning policy". Neither page claims precedence, so neither text settles which rule governs the cross-major case.

Concrete walk-through. Suppose the SDK decides it needs a feature that first ships in pydantic 3.0, so the floor moves from pydantic>=2.12.0 to pydantic>=3. (1) A maintainer following versioning.md's non-breaking list ships this in mcp 2.(X+1).0 — the text permits any minimum-version raise motivated by needed functionality. (2) A downstream user who read DEPENDENCY_POLICY.md's "within the same major version" qualifier concluded such a raise would wait for mcp 3.0 and kept pydantic<3 pinned in their environment. (3) An unpinned mcp upgrade to the new minor makes their environment unresolvable, and each party can point at one of the two pages to justify their expectation. The whole point of these pages is to prevent exactly that dispute.

Strictly speaking the two statements are logically compatible — asserting the same-major case is minor doesn't assert the cross-major case is major; it's implicature, not contradiction. But either the qualifier is intentional (and versioning.md's unconditional wording is then wrong) or it isn't (and the qualifier is misleading scope-narrowing). One of the two sentences needs an edit either way.

Fix. Pick one: (a) if cross-major floor raises are meant to be minor-eligible, drop "within the same major version" from DEPENDENCY_POLICY.md line 18; or (b) if they're meant to wait for the next mcp major, add that case explicitly to versioning.md's breaking-change list (e.g. "raising a dependency floor across that dependency's major version"). Docs-only, one-sentence change in files this PR introduces — nothing breaks on merge, hence not blocking.

Comment thread RELEASE.md
Comment thread docs/versioning.md Outdated
- versioning.md: name all three PEP 440 pre-release opt-in routes,
  matching RELEASE.md's wording.
- versioning.md: say what a retired protocol feature does on each kind
  of connection instead of implying it keeps working everywhere.
- roadmap.md: conformance runs on pull requests and pushes to main,
  not "every push".
- RELEASE.md: the bump recipe said `uv lock --resolution lowest-direct`,
  which would floor the entire committed lock; the lock is a normal
  resolution and lowest-direct belongs only to the CI leg.

No-Verification-Needed: doc-only change

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/versioning.md">

<violation number="1" location="docs/versioning.md:57">
P3: The linked page does not yet describe each modern-connection outcome as claimed: protocol logging can be silently dropped unless the request opted in, not simply warn then fail. Add that per-revision behavior to `deprecated.md`, or narrow this cross-reference.</violation>
</file>

<file name="RELEASE.md">

<violation number="1" location="RELEASE.md:11">
P3: The lowest-direct CI run does write a temporary lockfile: its matrix command is `uv sync --upgrade --resolution lowest-direct`, and `uv sync --upgrade` updates `uv.lock`. The result is safely discarded with the CI checkout, but saying it is “never written to the lock” makes the release instructions inaccurate. Please distinguish “never committed” from “never written.”</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread docs/versioning.md

**SDK API deprecations** — a name or parameter this SDK is retiring. The API keeps working, marked with [`typing_extensions.deprecated`](https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.deprecated), so static type checkers flag every call site and Python emits a `DeprecationWarning` at runtime. A deprecated API survives at least one minor release with its warning in place, and is removed only in a major version: something deprecated during 2.x is not removed before 3.0.

**Protocol deprecations** — a feature the MCP specification has retired (for example the SEP-2577 set in the 2026-07-28 revision). The SDK keeps implementing these through the specification's deprecation window, but what still works depends on the revision a connection negotiated: on a connection speaking an older revision they behave as before; on a 2026-07-28 connection a retired feature may have no wire support left at all (server-initiated sampling and roots have no back-channel to travel over, and `ping` no longer exists), so the call warns and then fails. Either way the call site warns with `MCPDeprecationWarning`, a `UserWarning` subclass, so the warning is visible by default rather than hidden the way `DeprecationWarning` is outside `__main__`. **[Deprecated features](deprecated.md)** lists every one, exactly what happens on each kind of connection, its replacement, and how to silence the warning when you genuinely serve older clients.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The linked page does not yet describe each modern-connection outcome as claimed: protocol logging can be silently dropped unless the request opted in, not simply warn then fail. Add that per-revision behavior to deprecated.md, or narrow this cross-reference.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/versioning.md, line 57:

<comment>The linked page does not yet describe each modern-connection outcome as claimed: protocol logging can be silently dropped unless the request opted in, not simply warn then fail. Add that per-revision behavior to `deprecated.md`, or narrow this cross-reference.</comment>

<file context>
@@ -54,7 +54,7 @@ There are two kinds, warned differently on purpose.
 **SDK API deprecations** — a name or parameter this SDK is retiring. The API keeps working, marked with [`typing_extensions.deprecated`](https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.deprecated), so static type checkers flag every call site and Python emits a `DeprecationWarning` at runtime. A deprecated API survives at least one minor release with its warning in place, and is removed only in a major version: something deprecated during 2.x is not removed before 3.0.
 
-**Protocol deprecations** — a feature the MCP specification has retired (for example the SEP-2577 set in the 2026-07-28 revision). These keep working through the specification's deprecation window and warn with `MCPDeprecationWarning`, a `UserWarning` subclass, so the warning is visible by default rather than hidden the way `DeprecationWarning` is outside `__main__`. **[Deprecated features](deprecated.md)** lists every one, its replacement, and how to silence the warning when you genuinely serve older clients.
+**Protocol deprecations** — a feature the MCP specification has retired (for example the SEP-2577 set in the 2026-07-28 revision). The SDK keeps implementing these through the specification's deprecation window, but what still works depends on the revision a connection negotiated: on a connection speaking an older revision they behave as before; on a 2026-07-28 connection a retired feature may have no wire support left at all (server-initiated sampling and roots have no back-channel to travel over, and `ping` no longer exists), so the call warns and then fails. Either way the call site warns with `MCPDeprecationWarning`, a `UserWarning` subclass, so the warning is visible by default rather than hidden the way `DeprecationWarning` is outside `__main__`. **[Deprecated features](deprecated.md)** lists every one, exactly what happens on each kind of connection, its replacement, and how to silence the warning when you genuinely serve older clients.
 
 ## Supported release lines
</file context>

Comment thread RELEASE.md
runtime dependencies are dynamic and live under
`[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies`.
2. Upgrade lock with `uv lock --resolution lowest-direct`
2. Regenerate the lock with `uv lock` (or `uv lock --upgrade-package <package>`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The lowest-direct CI run does write a temporary lockfile: its matrix command is uv sync --upgrade --resolution lowest-direct, and uv sync --upgrade updates uv.lock. The result is safely discarded with the CI checkout, but saying it is “never written to the lock” makes the release instructions inaccurate. Please distinguish “never committed” from “never written.”

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At RELEASE.md, line 11:

<comment>The lowest-direct CI run does write a temporary lockfile: its matrix command is `uv sync --upgrade --resolution lowest-direct`, and `uv sync --upgrade` updates `uv.lock`. The result is safely discarded with the CI checkout, but saying it is “never written to the lock” makes the release instructions inaccurate. Please distinguish “never committed” from “never written.”</comment>

<file context>
@@ -8,7 +8,11 @@ move; this is the mechanics.
    runtime dependencies are dynamic and live under
    `[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies`.
-2. Upgrade lock with `uv lock --resolution lowest-direct`
+2. Regenerate the lock with `uv lock` (or `uv lock --upgrade-package <package>`
+   to move just that package's locked version). The committed `uv.lock` is a
+   normal, newest-allowed resolution; the `lowest-direct` resolution that
</file context>

Comment thread CONTRIBUTING.md

Every new issue gets a first look from a maintainer within two business days. That first look is the *triage*: it means labeling the issue and deciding whether it is valid and actionable, not fixing it.

The labels follow the shared [MCP SDK taxonomy](https://modelcontextprotocol.io/community/sdk-tiers#issue-triage-labels): one **type** (`bug`, `enhancement`, `question`), one **status** (`needs confirmation`, `needs repro`, `ready for work`, `good first issue`, `help wanted`), and — once actionable — one **priority**:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The closed label taxonomy this section publishes ('one type, one status, one priority') is contradicted by the repo's own labels: pre-existing line 51 of this same file references a needs maintainer action status label the enumeration omits, and the issue-template automation applies labels outside it — feature-request.yaml auto-applies feature request (not enhancement, which no template applies), and bug.yaml auto-applies need confirmation (no 's', mismatching the documented needs confirmation) while applying no bug type label at all. Since this PR canonizes the taxonomy as the SEP-1730-measured contract, either align the enumeration and the templates' labels: fields with it, or document how the actual labels map onto it.

Extended reasoning...

What the inconsistency is. The new Issue Triage section (CONTRIBUTING.md:59) publishes a closed label taxonomy as a normative contract: "The labels follow the shared MCP SDK taxonomy: one type (bug, enhancement, question), one status (needs confirmation, needs repro, ready for work, good first issue, help wanted), and — once actionable — one priority". Two independent sources in the same repo contradict that enumeration: the same document's own pre-existing text, and the issue-template automation that actually applies labels to every new issue.

Contradiction 1 — within CONTRIBUTING.md itself. Line 51 (pre-existing) says "Issues labeled needs confirmation or needs maintainer action are not ready for work" — treating needs maintainer action as an in-use status label, grouped with needs confirmation (which is in the enumeration). But the closed status set published eight lines later omits it, and needs maintainer action appears nowhere else in the repo. A reader cannot map the label the document itself uses onto the taxonomy the document declares every issue follows.

Contradiction 2 — the issue-template automation. Verified in the checkout:

  • .github/ISSUE_TEMPLATE/feature-request.yaml line 3: labels: ["feature request"] — a label absent from the taxonomy, whose type label for this class is enhancement. No template applies enhancement, so it is absent from every template-created feature issue.
  • .github/ISSUE_TEMPLATE/bug.yaml line 3: labels: ["need confirmation"] — no 's', mismatching both the pre-existing line 51 and the new taxonomy at line 59 (both spell it needs confirmation). One of the two spellings is not a real label. The template also applies no bug type label at all, so a template-filed bug starts with only a (misspelled) status label.
  • question.yaml (labels: ["question"]) is the only template consistent with the taxonomy — confirming the mismatches are specific and real, not a misreading.

Step-by-step proof. (1) A user files a bug via the template; GitHub applies need confirmation and nothing else. (2) A triager or the SEP-1730 tier check follows the just-published contract and queries label:"needs confirmation" or expects one type label per issue — the query misses the issue, and the one-type invariant is already violated at creation. (3) Similarly, a contributor reading the taxonomy queries label:enhancement for feature work and finds nothing, because every template-filed feature request carries feature request instead. The contract published by this PR is falsified by the repo's own automation on every template-filed issue.

Why this belongs in this PR. The templates and line 51 pre-date the PR, but the PR is what introduces the closed enumeration and canonizes it as the taxonomy the SEP-1730 tier assessment measures — the contradiction only becomes a contradiction once this text merges. This is a policy-docs PR whose substance is textual precision, so reconciling is squarely in scope.

How to fix. Either (a) align reality with the taxonomy — add bug + needs confirmation to bug.yaml's labels:, change feature-request.yaml to enhancement, and either add needs maintainer action to the status enumeration or rewrite line 51 to use a listed label — or (b) keep the labels as they are and add a sentence mapping them onto the taxonomy. Option (a) is a few one-line edits and makes the published contract true.

Impact/severity. Nothing breaks at merge — this is a docs/process inconsistency, so nit severity. All six verifier confirmations (three per source bug) agreed on the facts and on nit; there were no refutations.

Comment thread docs/versioning.md
Comment on lines +53 to +55
There are two kinds, warned differently on purpose.

**SDK API deprecations** — a name or parameter this SDK is retiring. The API keeps working, marked with [`typing_extensions.deprecated`](https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.deprecated), so static type checkers flag every call site and Python emits a `DeprecationWarning` at runtime. A deprecated API survives at least one minor release with its warning in place, and is removed only in a major version: something deprecated during 2.x is not removed before 3.0.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The categorical claim that SDK API deprecations are "marked with typing_extensions.deprecated, so static type checkers flag every call site and Python emits a DeprecationWarning at runtime" (and "one release of warning at minimum before an SDK API is removed" below) is already false for an existing documented deprecation: SUPPORTED_PROTOCOL_VERSIONS in mcp_types.version is deprecated only in its docstring and in the migration guide, with no marker and no runtime warning — and typing_extensions.deprecated structurally cannot decorate a module-level constant. Either qualify the sentence (e.g. constants that cannot carry the marker are announced in the migration guide) or add a module __getattr__ that warns on access.

Extended reasoning...

What the page promises vs. what the SDK does. The new Deprecations section states categorically that SDK API deprecations are "marked with [typing_extensions.deprecated], so static type checkers flag every call site and Python emits a DeprecationWarning at runtime," and the closing bullet reinforces it: "one release of warning at minimum before an SDK API is removed." The SDK already has a formally documented SDK-API deprecation that goes through neither channel.

The counterexample. src/mcp-types/mcp_types/version.py:48-51 declares SUPPORTED_PROTOCOL_VERSIONS with the docstring "Deprecated: prefer HANDSHAKE_PROTOCOL_VERSIONS or MODERN_PROTOCOL_VERSIONS. Kept as the union for v1.x compatibility." It is listed in that module's __all__ (line 16) and reachable via the documented mcp.types.version path, so it is public surface under this page's own definition ("every name exported ... by mcp_types" plus documented import paths). docs/migration.md:552 gives it a dedicated section heading — "SUPPORTED_PROTOCOL_VERSIONS deprecated; LATEST_PROTOCOL_VERSION changed meaning" — so this is an official, documented deprecation, not an informal aside. It is not a protocol deprecation either: it does not appear in docs/deprecated.md and carries no MCPDeprecationWarning.

No warning mechanism exists. Grepping src/ shows every @deprecated usage is on a function or method (mostly the MCPDeprecationWarning protocol set); nothing marks this constant, and there is no module __getattr__ in mcp_types/version.py or the mcp/types alias. So type checkers do not flag call sites and Python emits no warning of any kind at runtime — the opposite of what this page promises.

Step-by-step proof. (1) A user reads this policy page and audits per its implied procedure: run the suite with warnings-as-errors and type-check with pyright, expecting every deprecated SDK API to surface. (2) Their code does from mcp.types.version import SUPPORTED_PROTOCOL_VERSIONS. (3) Pyright reports nothing (no @deprecated marker) and the test run emits no warning (plain module attribute access). (4) They conclude they use no deprecated SDK APIs. (5) At 3.0, the constant is removed — per the migration guide it is deprecated and "kept for v1.x compatibility" — and the user gets zero of the promised "one release of warning at minimum."

Why the named mechanism cannot cover this class. typing_extensions.deprecated is a decorator for callables and classes; it structurally cannot be applied to a module-level constant. So the categorical wording overlooks a class of deprecation the SDK already has, and the only runtime option for constants is a module __getattr__ shim — which the SDK does not use.

Fix (either side works). Docs-side: qualify the sentence, e.g. "functions, methods, and classes are marked with typing_extensions.deprecated; deprecations that cannot carry the marker, such as module constants, are announced in the migration guide." Code-side: add a module __getattr__ in mcp_types/version.py (and the mcp.types.version alias) that emits DeprecationWarning on SUPPORTED_PROTOCOL_VERSIONS access, which would make the page's claim true as written.

Severity. Docs-only imprecision in a policy page whose substance is the accuracy of these commitments — worth fixing in this PR since it introduces the claim, but nothing breaks at merge, so it is not blocking. All four verifiers confirmed independently; none refuted.

Comment thread DEPENDENCY_POLICY.md
Comment on lines +24 to +26
## Security in the SDK itself

Vulnerabilities in the SDK's own code — as opposed to its dependencies — follow the reporting process and response commitments in [SECURITY.md](https://github.com/modelcontextprotocol/python-sdk/blob/main/SECURITY.md).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The new "Security in the SDK itself" section says SDK vulnerabilities "follow the reporting process and response commitments in SECURITY.md", but SECURITY.md contains no response commitments — only a supported-versions table, the advisory reporting channel, and a what-to-include checklist. Either soften to "the reporting process in SECURITY.md" (matching how docs/versioning.md in this same PR accurately phrases the identical pointer), or add actual response commitments (acknowledgement window, triage/fix timeline) to SECURITY.md.

Extended reasoning...

The inaccuracy. DEPENDENCY_POLICY.md line 26 states that vulnerabilities in the SDK's own code "follow the reporting process and response commitments in SECURITY.md". SECURITY.md (unchanged by this PR) contains exactly three things: a supported-versions table, a "Reporting Security Issues" section (use GitHub Security Advisories; do not use public issues/discussions/PRs), and a "What to Include" checklist. There is no response commitment of any kind — no acknowledgement window, no triage SLA, no fix or disclosure timeline. The phrase "To help us triage and respond quickly" is a request to the reporter, not a commitment by maintainers.\n\nWhy the pointer dead-ends. The only response-time commitment this PR publishes anywhere is CONTRIBUTING.md's new triage table, where P0 (which includes High/Critical-severity security issues) is "resolved within 7 days". But that table explicitly governs the public issue tracker — and the very same section (plus SECURITY.md itself) says security reports must not go through the issue tracker. So a security reporter following the chain has no committed response at all: the private channel makes no commitments, and the channel with commitments is the one they're told not to use.\n\nThe accurate wording exists in this same PR. docs/versioning.md phrases the identical cross-reference correctly: "The security-specific version of this table, and how to report a vulnerability, is in SECURITY.md" — describing what the file actually contains, without asserting commitments that don't exist. That contrast shows this is a wording slip in DEPENDENCY_POLICY.md, not a deliberate claim.\n\nWhy it matters for this PR specifically. Per the PR description, these pages are written as "commitments rather than descriptions" for the SEP-1730 tier assessment. A tier assessor or vulnerability reporter who follows the pointer to learn what response to expect finds nothing, and can reasonably conclude the policy chain is broken — the one failure mode a policy-publication PR is trying to avoid.\n\nStep-by-step proof. (1) A researcher finds a vulnerability in the SDK's own code and reads DEPENDENCY_POLICY.md's "Security in the SDK itself" section, which promises "response commitments in SECURITY.md". (2) They open SECURITY.md and read it in full: supported-versions table → GitHub Security Advisory channel → what-to-include checklist. No commitment appears. (3) They check whether commitments live elsewhere and find CONTRIBUTING.md's P0 row ("resolved within 7 days") — but its scope is issue-tracker labels, and both CONTRIBUTING.md and SECURITY.md direct security reports away from the issue tracker. (4) The promised commitments do not exist anywhere the reporter is allowed to go. One charitable reading — that the supported-versions table ("which lines receive security fixes") is itself a "response commitment" — doesn't hold: that is a support-scope statement, not what a reporter would understand as a response commitment (what happens, and when, after filing).\n\nFix. Either (a) soften the sentence to "…follow the reporting process in SECURITY.md", a two-word deletion that makes it accurate, or (b) if response commitments are meant to exist for the tier assessment, add them to SECURITY.md (acknowledgement window, triage SLA, fix/disclosure timeline) so the pointer becomes true. Docs-only; nothing breaks at merge, hence not blocking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant